import os
import signal
import sys
from typing import Any, Awaitable, Callable


from .manager import Manager
from .storage import Storage
from .utils import printime, request


ALL_FINISH = "all_finish"


class Task:
    def __init__(self, url="", name="", need_save=True, priority=0):
        self.name = name or os.path.basename(url)
        self.url = url
        self.priority = priority  # 优先级
        self.need_save = need_save  # 是否需要保存
        self.finish_state = False  # 完成状态

    def __lt__(self, other):
        return self.priority < other.priority


class Spider:
    def __init__(
        self,
        task_func: Callable[[Task, bytes], Awaitable[None]],
        task_class=Task,
        task_run_count=100,
        task_delay=0,
        storage_path="db.sqlite3",
    ):
        self.__task_func = task_func
        self.__manager = Manager(run_func=self.__runTask, run_count=task_run_count, delay=task_delay)
        self.__storage = Storage(storage_path)

        # 捕获 ctrl+c
        signal.signal(signal.SIGINT, self.__exit)
        signal.signal(signal.SIGTERM, self.__exit)

        # 添加失败任务
        for info in self.__storage.getValues():
            if not isinstance(info, dict):
                continue

            task: Task = task_class()
            task.__dict__ = info
            if not task.finish_state:
                self.__manager.add(task, task.priority)

    def addTask(self, task: Task):
        if isinstance(task, str):
            task = Task(task)
        if self.getItem(task.name):
            return
        elif task.need_save:
            self.__storage.set(task.name, task.__dict__)

        self.__manager.add(task, task.priority)

    def runTask(self):
        printime("begin")
        self.__manager.run()
        if self.__manager.isfinish():
            self.__storage.set(ALL_FINISH, True)
        self.__storage.close()
        printime("end")

    async def __runTask(self, task: Task):
        try:
            data = await request(task.url)

            # 记录任务
            if task.need_save:
                task.finish_state = bool(data)
                self.__storage.set(task.name, task.__dict__)

            if data:
                await self.__task_func(task, data)
                return True
        except:
            pass
        return False

    def __exit(self, signal: int, frame: Any):
        printime("signal=%d" % signal)
        if self.__manager.hasrun():
            self.__manager.stop()
            self.__storage.save()
        else:
            self.__storage.close()
            sys.exit()

    def getItem(self, key: str):
        return self.__storage.get(key)
